package com.wuliu.system.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.wuliu.common.constant.Constants;
import com.wuliu.common.constant.OrderConstants;
import com.wuliu.common.exception.ServiceException;
import com.wuliu.common.utils.DateUtils;
import com.wuliu.common.utils.DictUtils;
import com.wuliu.common.utils.RedissonUtils;
import com.wuliu.common.utils.StringUtils;
import com.wuliu.system.domain.*;
import com.wuliu.system.domain.bo.AddTrackBo;
import com.wuliu.system.domain.bo.CalcOrderFeeBo;
import com.wuliu.system.mapper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 公共类
 *
 * @author realue
 * @version 1.0
 * @date 2023-10-24 17:02
 */
@Component
public class CommonService {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
    @Resource
    private BasNoCounterMapper basNoCounterMapper;
    @Resource
    private BasOrderRuleMapper basOrderRuleMapper;
    @Resource
    private RedissonUtils redissonUtils;
    @Resource
    private WeixinService weixinService;
    @Resource
    private BasQrcodeMapper basQrcodeMapper;
    @Resource
    private TspOrderPreMapper tspOrderPreMapper;
    @Resource
    private TspTrackMapper tspTrackMapper;
    @Resource
    private SysDeptFormulaMapper sysDeptFormulaMapper;
    @Resource
    private FileUploadService fileUploadService;

    /**
     * 生成单号
     *
     * @param sysAccount 账套号
     * @param type       类型
     * @param cnt        数量
     * @return 返回list
     */
    @Transactional(rollbackFor = Exception.class)
    public List<String> createOrderNo(String sysAccount, Integer type, Integer cnt) {
        List<String> orderNos = new ArrayList<>();
        BasOrderRule basOrderRule = basOrderRuleMapper.selectOne(new LambdaQueryWrapper<BasOrderRule>()
                .eq(BasOrderRule::getSysAccount, sysAccount)
                .eq(BasOrderRule::getType, type));
        if (null == basOrderRule) {
            throw new ServiceException("请先设置运单生成规则");
        }
        boolean lockFlag;
        String redisKey = Constants.GET_ORDER_NO_REDISSON_KEY + sysAccount + "-" + type;
        try {
            lockFlag = redissonUtils.tryLock(redisKey, TimeUnit.SECONDS, 5, 10);
            if (lockFlag) {
                BasNoCounter basNoCounter = basNoCounterMapper.selectOne(new LambdaQueryWrapper<BasNoCounter>()
                        .eq(BasNoCounter::getSysAccount, sysAccount)
                        .eq(BasNoCounter::getType, type));
                Long beginNo = 1L;
                if (null != basNoCounter) {
                    beginNo = basNoCounter.getCurrentValue();
                }
                int length = basOrderRule.getLength();
                if (StringUtils.isNotEmpty(basOrderRule.getPrefix())) {
                    length = length - basOrderRule.getPrefix().length();
                }
                String format = basOrderRule.getPrefix() + "%0" + length + "d";
                for (int i = 0; i < cnt; i++) {
                    String orderNo = String.format(format, beginNo);
                    orderNos.add(orderNo);
                    beginNo++;
                }
                BasNoCounter noCounter = new BasNoCounter();
                noCounter.setCurrentValue(beginNo);
                if (null != basNoCounter) {
                    noCounter.setId(basNoCounter.getId());
                    basNoCounterMapper.updateBasNoCounter(noCounter);
                } else {
                    noCounter.setType(type);
                    noCounter.setSysAccount(sysAccount);
                    basNoCounterMapper.insertBasNoCounter(noCounter);
                }
            } else {
                throw new ServiceException("获取单号失败,请稍后重试");
            }
        } finally {
            redissonUtils.unlock(redisKey);
        }
        return orderNos;
    }

    /**
     * 生成下单二维码
     *
     * @param deptId     站点编码
     * @param userId     业务员编号
     * @param sysAccount 账套号
     * @param type       1业务员码 2站点码
     * @return
     */
    public String getQrCode(Long deptId, Long userId, String sysAccount, Integer type) {
        String qrCodeUrl = "";
        Long id = type == 1 ? userId : deptId;
        QueryWrapper<BasQrcode> wrapper = new QueryWrapper<>();
        wrapper.eq("sys_account", sysAccount).eq("type", type).eq("qr_id", id);
        BasQrcode basQrcode = basQrcodeMapper.selectOne(wrapper);
        if (null != basQrcode) {
            qrCodeUrl = basQrcode.getQrCodeUrl();
        } else {
            try {
                //TODO 跳转页面
                String page = "pages/express/express";
                String scene = "";
                if (type == 1) {
                    scene = "uId=" + userId + "&ac=" + sysAccount;
                } else {
                    scene = "dId=" + deptId + "&ac=" + sysAccount;
                }
                qrCodeUrl = weixinService.getUnlimited(page, scene, sysAccount);
                BasQrcode qrcode = new BasQrcode();
                qrcode.setId(IdUtil.randomUUID());
                qrcode.setQrCodeUrl(qrCodeUrl);
                qrcode.setType(type);
                qrcode.setQrId(id);
                qrcode.setSysAccount(sysAccount);
                qrcode.setQrName("");
                qrcode.setCreateTime(DateUtils.getNowDate());
                qrcode.setCreateBy("system");
                basQrcodeMapper.insertBasQrcode(qrcode);
            } catch (Exception e) {
                throw new ServiceException("获取二维码失败:" + e.getMessage());
            }
        }
        return qrCodeUrl;
    }

    /**
     * 生成下单二维码(普通二维码)
     *
     * @param deptId     站点编码
     * @param userId     业务员编号
     * @param sysAccount 账套号
     * @param type       1业务员码 2站点码
     * @return
     */
    public String getQrCodeEx(Long deptId, Long userId, String sysAccount, Integer type) {
        String qrCodeUrl = "";
        Long id = type == 1 ? userId : deptId;
        QueryWrapper<BasQrcode> wrapper = new QueryWrapper<>();
        wrapper.eq("sys_account", sysAccount).eq("type", type).eq("qr_id", id);
        BasQrcode basQrcode = basQrcodeMapper.selectOne(wrapper);
        if (null != basQrcode) {
            qrCodeUrl = basQrcode.getQrCodeUrl();
        } else {
            try {
                String domain = DictUtils.getSysConfigValue(sysAccount, "weixin_applet_order_domain");
                String scene = "";
                if (type == 1) {
                    scene = "uId=" + userId + "&ac=" + sysAccount;
                } else {
                    scene = "dId=" + deptId + "&ac=" + sysAccount;
                }
                domain = domain + scene;
                QrConfig config = new QrConfig();
                config.setWidth(200).setHeight(200).setErrorCorrection(ErrorCorrectionLevel.H);
                byte[] image = QrCodeUtil.generatePng(domain, config);
                qrCodeUrl = fileUploadService.uploadFile(image, "");
                BasQrcode qrcode = new BasQrcode();
                qrcode.setId(IdUtil.randomUUID());
                qrcode.setQrCodeUrl(qrCodeUrl);
                qrcode.setType(type);
                qrcode.setQrId(id);
                qrcode.setSysAccount(sysAccount);
                qrcode.setQrName("");
                qrcode.setCreateTime(DateUtils.getNowDate());
                qrcode.setCreateBy("system");
                basQrcodeMapper.insertBasQrcode(qrcode);
            } catch (Exception e) {
                throw new ServiceException("获取二维码失败:" + e.getMessage());
            }
        }
        return qrCodeUrl;
    }

    /**
     * 生成回单
     *
     * @return
     */
    public List<TspOrder> createSubOrder(TspOrder order) {
        List<TspOrder> newOrders = new ArrayList<>();
        for (int i = 1; i < order.getCargoCnt(); i++) {
            TspOrder newOrder = new TspOrder();
            BeanUtil.copyProperties(order, newOrder);
            newOrder.setId(IdUtil.randomUUID());
            newOrder.setOrderNo(order.getOrderNo() + "-" + i);
            newOrders.add(newOrder);
        }
        return newOrders;
    }

    /**
     * 计算运费
     *
     * @param dto
     * @return
     */
    public BigDecimal calcFee(CalcOrderFeeBo dto) {
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("srcProvince", dto.getSrcProvince());
            map.put("srcCity", dto.getSrcCity());
            map.put("dstProvince", dto.getDstProvince());
            map.put("dstCity", dto.getDstCity());
            map.put("sysAccount", dto.getSysAccount());
            map.put("cargoType", dto.getCargoType());
            SysDeptFormula formula = sysDeptFormulaMapper.selectSysDeptFormulaOne(map);
            if (null == formula) {
                throw new ServiceException("没有查询到运费公式");
            }
            BigDecimal volume = dto.getVolume();
            return execFormula(formula.getContent(), dto.getCargoCnt(), dto.getWeight(), volume);
        } catch (ScriptException e) {
            throw new ServiceException("运费查询失败");
        }
    }

    /**
     * 运行计算公式
     *
     * @param content  公式内容
     * @param cargoCnt 件数
     * @param weight   重量
     * @param volume   体积
     * @return
     * @throws ScriptException
     */
    public BigDecimal execFormula(String content, Integer cargoCnt, BigDecimal weight, BigDecimal volume) throws ScriptException {
        if (StringUtils.isEmpty(content)) {
            return BigDecimal.ZERO;
        }
        if (weight != null && weight.compareTo(BigDecimal.ZERO) > 0) {
            content = content.replaceAll("重量", weight.toString());
        } else if (weight.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        if (cargoCnt != null && cargoCnt > 0) {
            content = content.replaceAll("件数", cargoCnt.toString());
        }
        if (volume != null && volume.compareTo(BigDecimal.ZERO) > 0) {
            content = content.replaceAll("体积", volume.toString());
        }
        content = content.toLowerCase();
        content = content.replaceAll("int", "Math.floor")
                .replaceAll("iup", "Math.ceil")
                .replaceAll("round", "Math.round")
                .replaceAll("maxnum", "Math.max")
                .replaceAll("minnum", "Math.min")
                .replaceAll("ntz", "this.ntz");
        content = " function ntz(str)" +
                "  {" +
                "  var num = eval(str);" +
                "  if(num <0)" +
                "  {" +
                "  num = 0;" +
                "  } return num;" +
                "  };  num = " + content;
        jse.eval(content);
        BigDecimal retFee = new BigDecimal(jse.get("num").toString()).setScale(2, RoundingMode.HALF_UP);
        return retFee;
    }

    /**
     * 校验运单有效性
     *
     * @param orderNo
     * @param deptNo
     * @param sysAccount
     * @return
     */
    public Boolean checkOrderNo(String orderNo, String deptNo, String sysAccount) {
        long count = tspOrderPreMapper.selectCount(new LambdaQueryWrapper<TspOrderPre>()
                .eq(TspOrderPre::getSysAccount, sysAccount)
                .eq(TspOrderPre::getOrderNo, orderNo));
        if (count > 0) {
            throw new ServiceException("该单号已下单或已取消");
        }
        //TODO 是否为站点运单号
        return true;
    }

    /**
     * 构建路由实体类
     *
     * @param trackBo
     */
    public TspTrack buildTspTrack(AddTrackBo trackBo) {
        TspTrack tspTrack = new TspTrack();
        BeanUtil.copyProperties(trackBo, tspTrack);
        tspTrack.setId(IdUtil.randomUUID());
        tspTrack.setCreateTime(DateUtils.getNowDate());
        String operation = "";
        switch (trackBo.getOpCode()) {
            case OrderConstants.ORDER_STATUS_1:
                operation = "[" + trackBo.getDeptName() + "-" + trackBo.getOpUserName() + "]已收件";
                break;
            case OrderConstants.ORDER_STATUS_4:
                operation = "快件从[" + trackBo.getDeptName() + "]发出,下一站是[" + trackBo.getNextDeptName() + "],扫描员是[" + trackBo.getOpUserName() + "]";
                break;
            case OrderConstants.ORDER_STATUS_5:
                operation = "快件已到达[" + trackBo.getDeptName() + "],上一站是[" + trackBo.getPrevDeptName() + "],扫描员是[" + trackBo.getOpUserName() + "]";
                break;
            case OrderConstants.ORDER_STATUS_8:
                operation = "[" + trackBo.getDeptName() + "-" + trackBo.getOpUserName() + "][电话:" + trackBo.getOpUserTel() + "]正在派件";
                break;
            case OrderConstants.ORDER_STATUS_10:
                operation = "快收已由[图片签收]签收,签收网点是[" + trackBo.getDeptName() + "]";
        }
        tspTrack.setOperation(operation);
        return tspTrack;
    }

    /**
     * 构建文件上传类
     *
     * @param relationId
     * @param fileUrl
     * @param fileType
     * @param sysAccount
     * @return
     */
    public BasFiles buildBasFile(String relationId, String fileUrl, Integer fileType, String sysAccount) {
        BasFiles basFiles = new BasFiles();
        basFiles.setId(IdUtil.randomUUID());
        basFiles.setRelationId(relationId);
        basFiles.setFileType(fileType);
        basFiles.setFileUrl(fileUrl);
        basFiles.setType(1);
        basFiles.setSysAccount(sysAccount);
        basFiles.setCreateTime(DateUtils.getNowDate());
        return basFiles;
    }
}
